package com.deductive.web.disposition.special;

import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.deductive.common.core.controller.BaseController;
import com.deductive.common.core.domain.AjaxResult;
import com.deductive.common.core.page.TableDataInfo;
import com.deductive.domain.special.SpecialData;
import com.deductive.domain.special.SpecialTableColumn;
import com.deductive.mapper.PgDBMapper;
import com.deductive.service.special.SpecialDataService;
import com.deductive.service.special.SpecialTableColumnService;
import com.deductive.service.special.special_import.service.GdalService;
import com.deductive.service.special.special_import.service.GeoserverPublishService;
import com.deductive.service.special.special_import.tool.LayerInfo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.*;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;


@RestController
@RequestMapping("/desdSpecialData")
@Api(tags = "专题数据管理")
public class SpecialDataController extends BaseController {


    private static final Logger logger = LoggerFactory.getLogger(SpecialDataController.class);

    @Autowired
    private GdalService gdalService;

    @Autowired
    private GeoserverPublishService geoserverPublishService;

    @Autowired
    private PgDBMapper pgDBMapper;

    @Autowired
    private SpecialDataService desdSpecialDataService;

    @Autowired
    private SpecialTableColumnService desdTableColumnService;

    @Value("${ruoyi.profile}")
    private String UPLOAD_PATH;

    @Value("${geoserver.workspace}")
    private String geoserverWorkspace;

    @Value("${geoserver.storage}")
    private String geoserverStorage;


    /**
     * 分页查询所有数据
     *
     * @return 所有数据
     */
    @ApiOperation("专题数据分页查询")
    @GetMapping("/selectPageAll")
    public TableDataInfo selectPageAll(SpecialData desdSpecialData) {
        startPage();
        LambdaQueryWrapper<SpecialData> wq = new LambdaQueryWrapper<SpecialData>()
                .like(desdSpecialData.getTableName() != null, SpecialData::getTableName, desdSpecialData.getTableName())
                .orderByAsc(true, SpecialData::getTableName)
                .orderByDesc(true, SpecialData::getCreateTime);
        List<SpecialData> list = this.desdSpecialDataService.list(wq);
        return getDataTable(list);

    }


    /**
     * 通过主键查询单条数据
     *
     * @param id 主键
     * @return 单条数据
     */
    @ApiOperation("根据Id查询结果")
    @GetMapping("{id}")
    public AjaxResult selectOne(@PathVariable Serializable id) {
        return AjaxResult.success(this.desdSpecialDataService.getById(id));
    }

    /**
     * 新增数据
     *
     * @return 新增结果
     */
    @ApiOperation("专题数据新增")
    @PostMapping
    public AjaxResult add(@RequestBody SpecialData desdSpecialData) {
        return AjaxResult.success(this.desdSpecialDataService.save(desdSpecialData));
    }

    /**
     * 修改数据
     *
     * @return 修改结果
     */
    @ApiOperation("专题数据修改")
    @PutMapping
    public AjaxResult update(@RequestBody SpecialData desdSpecialData) {
        return AjaxResult.success(this.desdSpecialDataService.updateById(desdSpecialData));
    }

    /**
     * 删除数据
     *
     * @param ids 主键结合
     * @return 删除结果
     */
    @ApiOperation("专题数据删除")
    @DeleteMapping("/{ids}")
    public AjaxResult delete(@PathVariable Long[] ids) {
        List<Long> list = new ArrayList<>(Arrays.asList(ids));
        for (Long id : list) {
            SpecialData specialData = desdSpecialDataService.getById(id);
            String tableName = specialData.getTableName();
            //删除数据库表
            pgDBMapper.deleteTable(tableName);
            //删除表字段
            LambdaQueryWrapper<SpecialTableColumn> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(true,SpecialTableColumn::getTableName,tableName);
            desdTableColumnService.remove(queryWrapper);

        }
        return AjaxResult.success(this.desdSpecialDataService.removeByIds(list));
    }

    @ApiOperation("专题数据状态修改")
    @PutMapping("/updateStatus/{id}")
    public AjaxResult updateStatus(@PathVariable Integer id,
                                   @RequestParam(required = false) Integer status,
                                   @RequestParam(required = false) Integer isTree) {
        // 检查至少有一个参数不为空
        if (status == null && isTree == null) {
            return AjaxResult.error("至少需要更新一个状态字段");
        }

        LambdaUpdateWrapper<SpecialData> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(SpecialData::getId, id);

        if (status != null) {
            updateWrapper.set(SpecialData::getStatus, status);
        }
        if (isTree != null) {
            updateWrapper.set(SpecialData::getIsTree, isTree);
        }

        boolean updateResult = this.desdSpecialDataService.update(updateWrapper);
        return AjaxResult.success(updateResult ? "更新成功" : "更新失败", updateResult);
    }

    @ApiOperation("数据上传")
    @PostMapping("/uploadGdbZip")
    public AjaxResult uploadGdbZip(@RequestPart("file") MultipartFile file) {
//        @RequestPart("file") MultipartFile file,
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(20, 40, 60, TimeUnit.SECONDS, new LinkedBlockingDeque<>());


        String zipFilePath = null;
        List<String> gdbPaths = null;

        try {
            // 1. 检查上传的文件
            if (file.isEmpty()) {
                return error("请选择要上传的文件");
            }

            String zipFileName = file.getOriginalFilename();
            if (zipFileName == null || !zipFileName.toLowerCase().endsWith(".zip")) {
                return error("只支持上传ZIP格式的文件");
            }

            zipFilePath = Paths.get(UPLOAD_PATH, zipFileName).toString();

            // 2. 检查目标目录是否存在
            Path uploadDir = Paths.get(UPLOAD_PATH);
            if (!Files.exists(uploadDir)) {
                Files.createDirectories(uploadDir);
            }

            // 3. 检查并删除同名文件
            deleteExistingFiles(zipFileName);

            // 4. 保存上传的ZIP文件
            File zipFile = new File(zipFilePath);
            file.transferTo(zipFile);

            // 5. 解压ZIP文件
            gdbPaths = unzip(zipFilePath);
            for (String gdbPath : gdbPaths) {
                try {
                    if (gdbPath == null) {
                        return error("未在ZIP文件中找到有效的GDB文件");
                    }
                    // 获取图层信息
                    List<LayerInfo> layerInfos = gdalService.getLayersFromGdb(gdbPath);

                    boolean uploadSuccess;
                    // 检查数据库中是否存在同名表
                    if (checkExistingTables(layerInfos)) {
                        cleanupFiles(zipFilePath, gdbPath);
                        uploadSuccess = true;
//                        return error("数据库中已存在相同名称的表，请先删除现有表或重命名");
                    } else {
                        // 7. 上传GDB到PostgreSQL
                        uploadSuccess = gdalService.uploadGdbToPostgres(gdbPath, layerInfos);
                        if (uploadSuccess) {
                            // 记录图层信息
                            for (LayerInfo layerInfo : layerInfos) {
                                logger.info("图层: {}, 几何类型: {}",
                                        layerInfo.getLayerName(),
                                        layerInfo.getGeometryType());


                                long count = Long.parseLong(layerInfo.getCount());
                                // 异步发布到GeoServer
                                String epsg = layerInfo.getEpsg();
                                String layerName = layerInfo.getLayerName();
                                // 异步同步到Neo4j
//                                spatialDataSyncService.syncTableToNeo4j(layerName);
                                // todo 将信息同步到mysql数据表 desd_special_data
                                SpecialData desdSpecialData = new SpecialData();
                                String dataType = geometryType(layerInfo.getGeometryType());
                                desdSpecialData.setDataType(dataType);
                                desdSpecialData.setTableName(layerInfo.getLayerName());
                                desdSpecialData.setCreateTime(new Date());
                                desdSpecialData.setCount(count);
                                desdSpecialData.setEpsg(epsg);
                                desdSpecialDataService.save(desdSpecialData);
                            }

                            cleanupFiles(zipFilePath, gdbPath);
                        } else {
                            return error("数据上传失败");
                        }
                    }

                } catch (NumberFormatException e) {
                    cleanupFiles(zipFilePath, gdbPath);
                    return error("上传失败: " + e.getMessage());
                }
            }

            String format = String.format(
                    "{\"message\":\"数据上传成功，正在异步处理\",\"wmsUrls\":%s}",
                    geoserverPublishService.getSuccessUrls().toString());
            logger.info(format);
            return success();
        } catch (Exception e) {
//            cleanupFiles(zipFilePath, gdbPath);
            return error("上传失败: " + e.getMessage());
        }
    }

    /**
     * 检查数据库中是否存在同名表
     */
    private boolean checkExistingTables(List<LayerInfo> layerInfos) {
        try {
            for (LayerInfo layerInfo : layerInfos) {
                String layerName = layerInfo.getLayerName();
                JSONObject jsonObject = pgDBMapper.checkExistingTables(layerName);
                if (jsonObject != null) {
                    logger.warn("数据库中已存在表: {}", layerInfo.getLayerName());
                    return true;
                }
            }
            return false;
        } catch (Exception e) {
            logger.error("检查数据库表时发生错误", e);
            throw new RuntimeException("检查数据库表失败: " + e.getMessage());
        }
    }

    /**
     * 清理上传的文件和解压的文件夹
     *
     * @param zipFilePath ZIP文件路径
     * @param gdbPath     GDB文件夹路径
     */
    private void cleanupFiles(String zipFilePath, String gdbPath) {
        try {
            // 删除ZIP文件
            if (zipFilePath != null) {
                Path zipPath = Paths.get(zipFilePath);
                if (Files.exists(zipPath)) {
                    Files.delete(zipPath);
                    logger.info("已删除ZIP文件: {}", zipFilePath);
                }
            }

            // 删除GDB文件夹
            if (gdbPath != null) {
                Path gdbDirPath = Paths.get(gdbPath);
                if (Files.exists(gdbDirPath)) {
                    deleteDirectory(gdbDirPath.toFile());
                    logger.info("已删除GDB文件夹: {}", gdbPath);
                }
            }

        } catch (Exception e) {
            logger.error("清理文件时发生错误", e);
        }
    }

    /**
     * 递归删除目录及其内容
     *
     * @param directory 要删除的目录
     */
    private void deleteDirectory(File directory) {
        try {
            File[] files = directory.listFiles();
            if (files != null) {
                for (File file : files) {
                    if (file.isDirectory()) {
                        // 递归删除子目录
                        deleteDirectory(file);
                    } else {
                        // 删除文件
                        if (file.delete()) {
                            logger.debug("已删除文件: {}", file.getAbsolutePath());
                        } else {
                            logger.warn("无法删除文件: {}", file.getAbsolutePath());
                        }
                    }
                }
            }
            // 删除空目录
            if (directory.delete()) {
                logger.debug("已删除目录: {}", directory.getAbsolutePath());
            } else {
                logger.warn("无法删除目录: {}", directory.getAbsolutePath());
            }
        } catch (Exception e) {
            logger.error("删除目录时发生错误: {}", directory.getAbsolutePath(), e);
        }
    }


    private void deleteExistingFiles(String zipFileName) {
        // 删除同名的ZIP文件
        File zipFile = new File(UPLOAD_PATH + "\\" + zipFileName);
        if (zipFile.exists()) {
            zipFile.delete();
        }

        // 删除解压后的文件夹
        String gdbFolderName = zipFileName.replace(".zip", ".gdb");
        File gdbFolder = new File(UPLOAD_PATH + "\\" + gdbFolderName);
        if (gdbFolder.exists()) {
            deleteDirectory(gdbFolder);
        }
    }

    public List<String> unzip(String zipFilePath) throws IOException {
        Path destDirPath = Paths.get(UPLOAD_PATH);
        if (!Files.exists(destDirPath)) {
            Files.createDirectories(destDirPath);
        }
        Set<String> gdbPaths = new HashSet<>();

        try (ZipFile zipFile = new ZipFile(zipFilePath)) {
            Enumeration<? extends ZipArchiveEntry> entries = zipFile.getEntries();

            while (entries.hasMoreElements()) {
                ZipArchiveEntry entry = entries.nextElement();
                String entryName = entry.getName();
                Path entryPath = destDirPath.resolve(entryName).normalize();

                // 安全检查：确保解压的文件在目标目录内
                if (!entryPath.startsWith(destDirPath)) {
                    System.out.println("Warning: Skipping entry outside of target dir: " + entryName);
                    continue;
                }

                if (entry.isDirectory()) {
                    if (entryName.endsWith(".gdb/") || entryName.endsWith(".gdb\\")) {
                        String path = entryPath.toString();
                        if (!gdbPaths.contains(path)) {
                            gdbPaths.add(path);
                            System.out.println("Found GDB folder: " + path);
                        }
                    }
                    Files.createDirectories(entryPath);
                } else {
                    Files.createDirectories(entryPath.getParent());
                    try (InputStream is = zipFile.getInputStream(entry)) {
                        Files.copy(is, entryPath, StandardCopyOption.REPLACE_EXISTING);
                    }

                    // 检查父目录是否是.gdb文件夹
                    String parentPath = entryPath.getParent().toString();
                    if (parentPath.endsWith(".gdb/") || parentPath.endsWith(".gdb\\")) {
                        if (!gdbPaths.contains(parentPath)) {
                            gdbPaths.add(parentPath);
                            System.out.println("Found GDB path from file: " + parentPath);
                        }
                    }
                }
            }
        }

        return new ArrayList<>(gdbPaths);
    }


    private String geometryType(String gdbGeometryType) {
        switch (gdbGeometryType.toUpperCase()) {
            case "POINT":
            case "MULTIPOINT":
                return "点";
            case "LINESTRING":
            case "MULTILINESTRING":
                return "线";
            case "POLYGON":
            case "MULTIPOLYGON":
                return "面";
            default:
                return "GEOMETRY";
        }
    }

}
